package tomoto.util;

import java.lang.reflect.Array;

/**
 * Helper utility to operate arrays.
 * 
 * @author Tomoto Shimizu Washio
 * 
 */
public class ArrayUtil {
	@Deprecated
	private ArrayUtil() {}

	/**
	 * Allocates a new Object array of the specified type and length. This
	 * function is useful to avoid an unchecked conversion warning.
	 * 
	 * @param <T> element type
	 * @param type element type
	 * @param length length of array
	 * @return newly allocated array
	 */
	@SuppressWarnings("unchecked")
	public static <T> T[] newArrayOf(Class<T> type, int length) {
		return (T[]) Array.newInstance(type, length);
	}

	/**
	 * Duplicates an Object array. Equivalent to Arrays#copyOf in J2SE 6.
	 * 
	 * @param <T> Element type
	 * @param array array to duplicate
	 * @return newly allocated array
	 */
	public static <T> T[] dup(T[] array) {
		return dup(array, array.length);
	}

	/**
	 * Duplicates an Object array. Equivalent to Arrays#copyOf in J2SE 6.
	 * 
	 * @param <T> Element type
	 * @param array array to duplicate
	 * @param newLength length of the new array
	 * @return newly allocated array
	 */
	@SuppressWarnings("unchecked")
	public static <T> T[] dup(T[] array, int newLength) {
		T[] result = newArrayOf((Class<T>) array.getClass().getComponentType(), newLength);
		System.arraycopy(array, 0, result, 0, Math.min(array.length, newLength));
		return result;
	}

	/**
	 * Deeply duplicates an array of duplicatable objects.
	 * 
	 * @param <T> Element type, must be {@link Duplicatable}
	 * @param array array to duplicate
	 * @param newLength length of the new array
	 * @return newly allocated array
	 */
	@SuppressWarnings("unchecked")
	public static <T extends Duplicatable<T>> T[] deepDup(T[] array, int newLength) {
		T[] result = newArrayOf((Class<T>) array.getClass().getComponentType(), newLength);
		deepCopy(array, 0, result, 0, Math.min(array.length, newLength));
		return result;
	}

	/**
	 * Deeply duplicates an array of duplicatable objects.
	 * 
	 * @param <T> Element type, must be {@link Duplicatable}
	 * @param array array to duplicate
	 * @return newly allocated array
	 */
	public static <T extends Duplicatable<T>> T[] deepDup(T[] array) {
		return deepDup(array, array.length);
	}

	/**
	 * Duplicates an int array. Equivalent to Arrays#copyOf in J2SE 6.
	 * 
	 * @param array array to duplicate
	 * @return newly allocated array
	 */
	public static int[] dup(int[] array) {
		int[] result = new int[array.length];
		System.arraycopy(array, 0, result, 0, result.length);
		return result;
	}

	/**
	 * Duplicates a boolean array. Equivalent to Arrays#copyOf in J2SE 6.
	 * 
	 * @param array array to duplicate
	 * @return newly allocated array
	 */
	public static boolean[] dup(boolean[] array) {
		boolean[] result = new boolean[array.length];
		System.arraycopy(array, 0, result, 0, result.length);
		return result;
	}

	/**
	 * Copies the whole content of an array to another.
	 * 
	 * @param src source array
	 * @param dest destination array (length must be >= src.length)
	 * @throws ArrayIndexOutOfBoundsException dest does not have a sufficient
	 *             length
	 */
	public static void copy(Object src, Object dest) {
		System.arraycopy(src, 0, dest, 0, Array.getLength(src));
	}

	/**
	 * Deeply copies the whole content of an array to another.
	 * 
	 * @param <T> element type
	 * @param src source array
	 * @param dest destination array (length must be >= src.length)
	 * @throws ArrayIndexOutOfBoundsException dest does not have a sufficient
	 *             length
	 */
	public static <T extends Duplicatable<T>> void deepCopy(T[] src, T[] dest) {
		deepCopy(src, 0, dest, 0, src.length);
	}

	/**
	 * Deeply copies the specified part of an array to another.
	 * 
	 * @param <T> element type
	 * @param src source array
	 * @param srcPos position of source array to start copying
	 * @param dest destination array
	 * @param destPos position of destination array to start copying
	 * @param length length to copy
	 */
	public static <T extends Duplicatable<T>> void deepCopy(T[] src, int srcPos, T[] dest,
			int destPos, int length) {
		while (length-- > 0) {
			T o = src[srcPos++];
			dest[destPos++] = o != null ? o.dup() : null;
		}
	}

	/**
	 * Returns the index of the minimum value.
	 * 
	 * @param array array
	 * @return as described above
	 */
	public static int findMinIndex(int[] array) {
		int result = 0;
		int minValue = Integer.MAX_VALUE;
		for (int i = 0; i < array.length; i++) {
			if (array[i] < minValue) {
				result = i;
				minValue = array[i];
			}
		}
		return result;
	}
}
